package com.duobaoyu.dby.supply.biz.strategy;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.duobaoyu.dby.channel.adapter.enums.biz.order.OrderStatusEnum;
import com.duobaoyu.dby.channel.adapter.enums.biz.order.OrderTypeEnum;
import com.duobaoyu.dby.channel.common.util.SupplyCollectionUtils;
import com.duobaoyu.dby.supply.biz.client.boundary.order.OrderBoundaryClient;
import com.duobaoyu.dby.supply.biz.client.boundary.order.bean.bo.OrderInfoBO;
import com.duobaoyu.dby.supply.biz.constant.OperateSourceConstants;
import com.duobaoyu.dby.supply.biz.enums.OperationLogTypeEnum;
import com.duobaoyu.dby.supply.biz.service.config.OperationLogService;
import com.duobaoyu.dby.supply.biz.util.DingDingExtUtils;
import com.duobaoyu.dby.supply.common.biz.bo.order.ChannelOrderDetailInBO;
import com.duobaoyu.dby.supply.common.biz.bo.order.ChannelOrderDetailOutBO;
import com.duobaoyu.dby.supply.common.biz.dto.config.OperationLogSaveDTO;
import com.duobaoyu.dby.supply.common.biz.dto.log.LogDiff;
import com.duobaoyu.dby.supply.core.framework.strategy.AbstractStrategy;
import com.duobaoyu.dby.supply.db.entity.order.Order;
import com.duobaoyu.dby.supply.db.entity.order.OrderProduct;
import com.duobaoyu.dby.supply.db.repository.order.OrderProductRepository;
import com.duobaoyu.dby.supply.db.repository.order.OrderRepository;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * 订单状态同步抽象策略
 *
 * @author fengshaoyu
 * @version 1.0.0
 * @date 2023/10/13 16:36
 */
@Slf4j
public abstract class AbstractSyncOrderStatusStrategy extends AbstractStrategy<OrderInfoBO, ChannelOrderDetailOutBO> {
    @Resource
    private OrderBoundaryClient orderBoundaryClient;
    @Resource
    private OrderRepository orderRepository;
    @Resource
    private OrderProductRepository orderProductRepository;
    @Resource
    private OperationLogService operationLogService;

    protected abstract Set<String> channelCodes();

    @Override
    public boolean support(Object... args) {
        if (ObjectUtils.isNotEmpty(args) && CollectionUtils.isNotEmpty(this.channelCodes())) {
            return Arrays.stream(args)
                    .filter(item -> item instanceof String)
                    .anyMatch(item -> this.channelCodes().contains((String) item));
        }
        return false;
    }

    @Override
    protected Object beforeHandler(OrderInfoBO orderInfo) {
        if (!this.beforeCheckParams(orderInfo)) {
            return null;
        }
        ChannelOrderDetailInBO channelOrderDetailIn = new ChannelOrderDetailInBO();
        channelOrderDetailIn.setOrderSn(orderInfo.getOrderSn());
        channelOrderDetailIn.setOrderTradeSn(orderInfo.getOrderTradeSn());
        channelOrderDetailIn.setChannelOrderSn(orderInfo.getChannelOrderSn());
        channelOrderDetailIn.setChannelCode(orderInfo.getChannelCode());
        channelOrderDetailIn.setChannelTradeOrderSn(orderInfo.getChannelTradeOrderSn());
        channelOrderDetailIn.setChannelOrderParentSn(orderInfo.getChannelOrderParentSn());
        channelOrderDetailIn.setOrderProductTypeSet(Sets.newHashSet(orderInfo.getOrderType()));
        if (OrderTypeEnum.isRechargeOrder(orderInfo.getOrderType())) {
            List<OrderProduct> list = orderProductRepository.list(Wrappers.lambdaQuery(OrderProduct.class).eq(OrderProduct::getOrderSn, orderInfo.getOrderSn()));
            OrderProduct orderProduct = SupplyCollectionUtils.getSingleDataFirst(list, OrderProduct::new);
            channelOrderDetailIn.setOriginalPrice(orderProduct.getOriginalPrice());
        }
        return orderBoundaryClient.getChannelOrderDetail(channelOrderDetailIn);
    }

    protected abstract Boolean beforeCheckParams(OrderInfoBO orderInfo);

    @Override
    protected ChannelOrderDetailOutBO handler(OrderInfoBO orderInfo, Object beforeResult) {
        if (Objects.isNull(beforeResult)) {
            return null;
        }
        return beforeResult instanceof ChannelOrderDetailOutBO ? (ChannelOrderDetailOutBO) beforeResult : null;
    }

    /**
     * 更新订单状态
     */
    protected boolean modifyOrderStatus(OrderInfoBO orderInfo, Integer channelOrderStatus) {
        // 校验状态
        OrderStatusEnum currentStatus = OrderStatusEnum.getOrderSubStatusEnum(orderInfo.getOrderStatus());
        OrderStatusEnum updateStatus = OrderStatusEnum.getOrderSubStatusEnum(channelOrderStatus);
        if (Objects.isNull(currentStatus) || Objects.isNull(updateStatus) || Objects.equals(currentStatus, updateStatus)) {
            return false;
        }
        boolean validTransition = OrderStatusEnum.isValidTransition(currentStatus, updateStatus);
        if (!validTransition) {
            if(log.isDebugEnabled()) {
                log.debug("订单状态同步,不支持状态变更: [新状态={},订单信息={}]", channelOrderStatus, JSON.toJSONString(orderInfo));
            }
            DingDingExtUtils.sendMarkdownMsg(String.format("订单状态同步,不支持状态变更: 旧状态=%s, 新状态=%s", currentStatus.getDescribe(), updateStatus.getDescribe()));
            return false;
        }
        boolean update = orderRepository.update(Wrappers.lambdaUpdate(Order.class)
                .set(Order::getUpdateTime, LocalDateTime.now())
                .set(Order::getOrderStatus, channelOrderStatus)
                .eq(Order::getOrderStatus, currentStatus.getValue())
                .eq(Order::getId, orderInfo.getId()));
        // 更新成功,记录日志
        if (update) {
            OperationLogSaveDTO operationLog = null;
            try {
                operationLog = OperationLogSaveDTO.builder().logType(OperationLogTypeEnum.ORDER.getValue()).source(OperateSourceConstants.JOB).scene("状态同步").bizKey(orderInfo.getOrderSn())
                        .diffList(Lists.newArrayList(new LogDiff("订单状态", currentStatus.getDescribe(), updateStatus.getDescribe()))).build();
                operationLogService.save(operationLog);
            } catch (Exception e) {
                log.warn("保存订单状态更新日志失败: {}", operationLog, e);
            }
        } else {
            log.info("订单状态同步任务,更新订单状态失败: [新状态={}, 订单={}]", channelOrderStatus, orderInfo);
        }
        return update;
    }


}
